#ifndef CASTAR_H
#define CASTAR_H

#include "CList.h"
#include "CReceiver.h"

#include "astar/private/map.h"

#include <fstream>

class CAStar
{
public:
   CAStar();
   virtual ~CAStar();

   inline void init(CNode * pBegin, CNode * pEnd);

   inline void setActualCostCalculator(IActualCostCalculator * pActualCostCalculator);
   
   inline void setEstimateCostCalculator(IEstimateCostCalculator * pEstimateCostCalculator);
   
   inline void setSuccessorSearcher(ISuccesssorSearcher * pSuccessorSearcher);

   bool scan();

protected:
   void handleInsert(CNode * pNode);

protected:
   CList mOpenList;
   CList mCloseList;

   ISuccesssorSearcher * mpSuccessorSearcher;
   IActualCostCalculator * mpActualCostCalculator;
   IEstimateCostCalculator * mpEstimateCostCalculator;

   CReceiver mReceiver;

   CNode * mpBegin;
   CNode * mpEnd;
};

CAStar::CAStar()
: mpSuccessorSearcher(NULL)
, mpActualCostCalculator(NULL)
, mpEstimateCostCalculator(NULL)
, mpBegin(NULL)
, mpEnd(NULL)
{
}

CAStar::~CAStar()
{
   mpBegin->release();
   mpEnd->release();
   mOpenList.releaseHeap();
   mCloseList.releaseHeap();
}

inline void CAStar::init(CNode * pBegin, CNode * pEnd)
{
   mpBegin = pBegin;
   mpEnd = pEnd;
   mpBegin->addRef();
   mpEnd->addRef();
}

inline void CAStar::setActualCostCalculator(IActualCostCalculator * pActualCostCalculator)
{
   mpActualCostCalculator = pActualCostCalculator;
}

inline void CAStar::setEstimateCostCalculator(IEstimateCostCalculator * pEstimateCostCalculator)
{
   mpEstimateCostCalculator = pEstimateCostCalculator;
}

inline void CAStar::setSuccessorSearcher(ISuccesssorSearcher * pSuccessorSearcher)
{
   mpSuccessorSearcher = pSuccessorSearcher;
}

bool CAStar::scan()
{
   if(
         mpBegin == NULL
      || mpEnd == NULL
      || mpSuccessorSearcher == NULL
      || mpActualCostCalculator == NULL
      || mpEstimateCostCalculator == NULL
     )
   {
      return false;
   }

   const std::vector<key> & receivedData = mReceiver.getData();

   cost weightBuffer;
   mpActualCostCalculator->calculate(mpBegin->id(), weightBuffer);
   mpBegin->setCostFromBeginToHere(weightBuffer);
   weightBuffer = 0;
   mpEstimateCostCalculator->calculate(mpBegin->id(), mpEnd->id(), weightBuffer);
   mpBegin->setCostFromHereToEnd(weightBuffer);
   mOpenList.insert(mpBegin);

   std::fstream logOut("log.txt", std::ios::out);
   std::fstream mapOut("map.txt", std::ios::out);
   char map [sizeof(gMap) / sizeof(gMap[0])][sizeof(gMap[0])] = {'.'};
   memset(map, '.', sizeof(gMap));

   bool bRet = true;
   while(true)
   {
      CNode * pCurrent = mOpenList.getAndRemoveBest();

      if(pCurrent == NULL)
      {
         logOut << "failed" << std::endl;
         bRet = false;
         break;
      }
      int x = 0;
      int y = 0;
      Key2XY(pCurrent->id(), x, y);
      map[x][y] = 'x';

      mCloseList.push_back(pCurrent);
      pCurrent->release();

      if(pCurrent->predecessor())
      {
         int preX = 0;
         int preY = 0;
         Key2XY(pCurrent->predecessor()->id(), preX, preY);
         logOut << pCurrent->predecessor()->id() << "(" << preX << ", " << preY << ")" << "<->" << pCurrent->id() << "(" << pCurrent->getCost() << ")" << "(" << x << ", " << y << ")" << std::endl;
      }

      if(pCurrent->id() == mpEnd->id())
      {
         break;
      }

      mReceiver.clear();

      mpSuccessorSearcher->getNext(pCurrent->id(), mReceiver);

      for(int i = 0; i < receivedData.size(); ++i)
      {
         CNode * pRoad = new CNode(receivedData[i]);
         if(pRoad != NULL)
         {
            pRoad->setPredecessor(pCurrent);

            weightBuffer = 0;
            mpActualCostCalculator->calculate(pRoad->id(), weightBuffer);
            pRoad->setCostFromBeginToHere(weightBuffer);

            weightBuffer = 0;
            mpActualCostCalculator->calculate(pCurrent->id(), pRoad->id(), weightBuffer);
            pRoad->setCostFromBeginToHere(pRoad->getCostFromBeginToHere() + weightBuffer);

            weightBuffer = 0;
            mpEstimateCostCalculator->calculate(pRoad->id(), mpEnd->id(), weightBuffer);
            pRoad->setCostFromHereToEnd(weightBuffer);

            pRoad->setCostFromBeginToHere(pRoad->getCostFromBeginToHere() + pCurrent->getCostFromBeginToHere());

            handleInsert(pRoad);
            pRoad->release();
         }
      }
   }


   CNode * last = mCloseList.back();
   while(true)
   {
      int x = 0;
      int y = 0;
      Key2XY(last->id(), x, y);
      map[x][y] = 'o';
      if(last->id() == mpBegin->id())
      {
         break;
      }
      last = last->predecessor();
   }
   int iYBound = sizeof(map[0]);
   int iXBound = sizeof(map) / sizeof(map[0]);
   for(int x = 0; x < iXBound; ++x)
   {
      for(int y = 0; y < iYBound; ++y)
      {
         mapOut << map[x][y];
      }
      mapOut << std::endl;
   }
   return bRet;
}

void CAStar::handleInsert(CNode * pNode)
{
   CNode * pExistInOpen = mOpenList.get(pNode->id());
   CNode * pExistInClose = mCloseList.get(pNode->id());
   if(pExistInClose == NULL)
   {
      if(pExistInOpen != NULL)
      {
         if(pExistInOpen->getCost() > pNode->getCost())
         {
            pExistInOpen->setPredecessor(pNode->predecessor());
         }
      }
      else
      {
         mOpenList.insert(pNode);
      }
   }
}

#endif//CASTAR_H